Completed
Push — dev ( e3ccb1...690143 )
by Sarah
10s
created

waves.js ➔ ... ➔ removeRipple   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 60

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
c 1
b 0
f 0
nc 5
nop 3
dl 0
loc 60
rs 8.6961

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
/*!
2
 * Waves v0.7.5
3
 * http://fian.my.id/Waves
4
 *
5
 * Copyright 2014-2016 Alfiana E. Sibuea and other contributors
6
 * Released under the MIT license
7
 * https://github.com/fians/Waves/blob/master/LICENSE
8
 */
9
10
;(function(window, factory) {
11
    'use strict';
12
13
    // AMD. Register as an anonymous module.  Wrap in function so we have access
14
    // to root via `this`.
15
    if (typeof define === 'function' && define.amd) {
16
        define([], function() {
17
            return factory.apply(window);
18
        });
19
    }
20
21
    // Node. Does not work with strict CommonJS, but only CommonJS-like
22
    // environments that support module.exports, like Node.
23
    else if (typeof exports === 'object') {
24
        module.exports = factory.call(window);
25
    }
26
27
    // Browser globals.
28
    else {
29
        window.Waves = factory.call(window);
30
    }
31
})(typeof global === 'object' ? global : this, function() {
32
    'use strict';
33
34
    var Waves            = Waves || {};
1 ignored issue
show
Bug introduced by
The variable Waves seems to be never initialized.
Loading history...
35
    var $$               = document.querySelectorAll.bind(document);
36
    var toString         = Object.prototype.toString;
37
    var isTouchAvailable = 'ontouchstart' in window;
38
39
40
    // Find exact position of element
41
    function isWindow(obj) {
42
        return obj !== null && obj === obj.window;
43
    }
44
45
    function getWindow(elem) {
46
        return isWindow(elem) ? elem : elem.nodeType === 9 && elem.defaultView;
47
    }
48
49
    function isObject(value) {
50
        var type = typeof value;
51
        return type === 'function' || type === 'object' && !!value;
52
    }
53
54
    function isDOMNode(obj) {
55
        return isObject(obj) && obj.nodeType > 0;
56
    }
57
58
    function getWavesElements(nodes) {
59
        var stringRepr = toString.call(nodes);
60
61
        if (stringRepr === '[object String]') {
62
            return $$(nodes);
63
        } else if (isObject(nodes) && /^\[object (Array|HTMLCollection|NodeList|Object)\]$/.test(stringRepr) && nodes.hasOwnProperty('length')) {
64
            return nodes;
65
        } else if (isDOMNode(nodes)) {
66
            return [nodes];
67
        }
68
69
        return [];
70
    }
71
72
    function offset(elem) {
73
        var docElem, win,
74
            box = { top: 0, left: 0 },
75
            doc = elem && elem.ownerDocument;
76
77
        docElem = doc.documentElement;
78
79
        if (typeof elem.getBoundingClientRect !== typeof undefined) {
80
            box = elem.getBoundingClientRect();
81
        }
82
        win = getWindow(doc);
83
        return {
84
            top: box.top + win.pageYOffset - docElem.clientTop,
85
            left: box.left + win.pageXOffset - docElem.clientLeft
86
        };
87
    }
88
89
    function convertStyle(styleObj) {
90
        var style = '';
91
92
        for (var prop in styleObj) {
93
            if (styleObj.hasOwnProperty(prop)) {
94
                style += (prop + ':' + styleObj[prop] + ';');
95
            }
96
        }
97
98
        return style;
99
    }
100
101
    var Effect = {
102
103
        // Effect duration
104
        duration: 750,
105
106
        // Effect delay (check for scroll before showing effect)
107
        delay: 200,
108
109
        show: function(e, element, velocity) {
110
111
            // Disable right click
112
            if (e.button === 2) {
113
                return false;
114
            }
115
116
            element = element || this;
117
118
            // Create ripple
119
            var ripple = document.createElement('div');
120
            ripple.className = 'waves-ripple waves-rippling';
121
            element.appendChild(ripple);
122
123
            // Get click coordinate and element width
124
            var pos       = offset(element);
125
            var relativeY = 0;
126
            var relativeX = 0;
127
            // Support for touch devices
128
            if('touches' in e && e.touches.length) {
129
                relativeY   = (e.touches[0].pageY - pos.top);
130
                relativeX   = (e.touches[0].pageX - pos.left);
131
            }
132
            //Normal case
133
            else {
134
                relativeY   = (e.pageY - pos.top);
135
                relativeX   = (e.pageX - pos.left);
136
            }
137
            // Support for synthetic events
138
            relativeX = relativeX >= 0 ? relativeX : 0;
139
            relativeY = relativeY >= 0 ? relativeY : 0;
140
141
            var scale     = 'scale(' + ((element.clientWidth / 100) * 3) + ')';
142
            var translate = 'translate(0,0)';
143
144
            if (velocity) {
145
                translate = 'translate(' + (velocity.x) + 'px, ' + (velocity.y) + 'px)';
146
            }
147
148
            // Attach data to element
149
            ripple.setAttribute('data-hold', Date.now());
150
            ripple.setAttribute('data-x', relativeX);
151
            ripple.setAttribute('data-y', relativeY);
152
            ripple.setAttribute('data-scale', scale);
153
            ripple.setAttribute('data-translate', translate);
154
155
            // Set ripple position
156
            var rippleStyle = {
157
                top: relativeY + 'px',
158
                left: relativeX + 'px'
159
            };
160
161
            ripple.classList.add('waves-notransition');
162
            ripple.setAttribute('style', convertStyle(rippleStyle));
163
            ripple.classList.remove('waves-notransition');
164
165
            // Scale the ripple
166
            rippleStyle['-webkit-transform'] = scale + ' ' + translate;
167
            rippleStyle['-moz-transform'] = scale + ' ' + translate;
168
            rippleStyle['-ms-transform'] = scale + ' ' + translate;
169
            rippleStyle['-o-transform'] = scale + ' ' + translate;
170
            rippleStyle.transform = scale + ' ' + translate;
171
            rippleStyle.opacity = '1';
172
173
            var duration = e.type === 'mousemove' ? 2500 : Effect.duration;
174
            rippleStyle['-webkit-transition-duration'] = duration + 'ms';
175
            rippleStyle['-moz-transition-duration']    = duration + 'ms';
176
            rippleStyle['-o-transition-duration']      = duration + 'ms';
177
            rippleStyle['transition-duration']         = duration + 'ms';
178
179
            ripple.setAttribute('style', convertStyle(rippleStyle));
1 ignored issue
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
180
        },
181
182
        hide: function(e, element) {
183
            element = element || this;
184
185
            var ripples = element.getElementsByClassName('waves-rippling');
186
187
            for (var i = 0, len = ripples.length; i < len; i++) {
188
                removeRipple(e, element, ripples[i]);
189
            }
190
191
            if (isTouchAvailable) {
192
                element.removeEventListener('touchend', Effect.hide);
193
                element.removeEventListener('touchcancel', Effect.hide);
194
            }
195
196
            element.removeEventListener('mouseup', Effect.hide);
197
            element.removeEventListener('mouseleave', Effect.hide);
198
        }
199
    };
200
201
    /**
202
     * Collection of wrapper for HTML element that only have single tag
203
     * like <input> and <img>
204
     */
205
    var TagWrapper = {
206
207
        // Wrap <input> tag so it can perform the effect
208
        input: function(element) {
209
210
            var parent = element.parentNode;
211
212
            // If input already have parent just pass through
213
            if (parent.tagName.toLowerCase() === 'i' && parent.classList.contains('waves-effect')) {
214
                return;
215
            }
216
217
            // Put element class and style to the specified parent
218
            var wrapper       = document.createElement('i');
219
            wrapper.className = element.className + ' waves-input-wrapper';
220
            element.className = 'waves-button-input';
221
222
            // Put element as child
223
            parent.replaceChild(wrapper, element);
224
            wrapper.appendChild(element);
225
226
            // Apply element color and background color to wrapper
227
            var elementStyle    = window.getComputedStyle(element, null);
228
            var color           = elementStyle.color;
229
            var backgroundColor = elementStyle.backgroundColor;
230
231
            wrapper.setAttribute('style', 'color:' + color + ';background:' + backgroundColor);
232
            element.setAttribute('style', 'background-color:rgba(0,0,0,0);');
233
234
        },
235
236
        // Wrap <img> tag so it can perform the effect
237
        img: function(element) {
238
239
            var parent = element.parentNode;
240
241
            // If input already have parent just pass through
242
            if (parent.tagName.toLowerCase() === 'i' && parent.classList.contains('waves-effect')) {
243
                return;
244
            }
245
246
            // Put element as child
247
            var wrapper  = document.createElement('i');
248
            parent.replaceChild(wrapper, element);
249
            wrapper.appendChild(element);
250
251
        }
252
    };
253
254
    /**
255
     * Hide the effect and remove the ripple. Must be
256
     * a separate function to pass the JSLint...
257
     */
258
    function removeRipple(e, el, ripple) {
259
260
        // Check if the ripple still exist
261
        if (!ripple) {
262
            return;
263
        }
264
265
        ripple.classList.remove('waves-rippling');
266
267
        var relativeX = ripple.getAttribute('data-x');
268
        var relativeY = ripple.getAttribute('data-y');
269
        var scale     = ripple.getAttribute('data-scale');
270
        var translate = ripple.getAttribute('data-translate');
271
272
        // Get delay beetween mousedown and mouse leave
273
        var diff = Date.now() - Number(ripple.getAttribute('data-hold'));
274
        var delay = 350 - diff;
275
276
        if (delay < 0) {
277
            delay = 0;
278
        }
279
280
        if (e.type === 'mousemove') {
281
            delay = 150;
282
        }
283
284
        // Fade out ripple after delay
285
        var duration = e.type === 'mousemove' ? 2500 : Effect.duration;
286
287
        setTimeout(function() {
288
289
            var style = {
290
                top: relativeY + 'px',
291
                left: relativeX + 'px',
292
                opacity: '0',
293
294
                // Duration
295
                '-webkit-transition-duration': duration + 'ms',
296
                '-moz-transition-duration': duration + 'ms',
297
                '-o-transition-duration': duration + 'ms',
298
                'transition-duration': duration + 'ms',
299
                '-webkit-transform': scale + ' ' + translate,
300
                '-moz-transform': scale + ' ' + translate,
301
                '-ms-transform': scale + ' ' + translate,
302
                '-o-transform': scale + ' ' + translate,
303
                'transform': scale + ' ' + translate
304
            };
305
306
            ripple.setAttribute('style', convertStyle(style));
307
308
            setTimeout(function() {
309
                try {
310
                    el.removeChild(ripple);
1 ignored issue
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
311
                } catch (e) {
312
                    return false;
313
                }
314
            }, duration);
315
316
        }, delay);
317
    }
318
319
320
    /**
321
     * Disable mousedown event for 500ms during and after touch
322
     */
323
    var TouchHandler = {
324
325
        /* uses an integer rather than bool so there's no issues with
326
         * needing to clear timeouts if another touch event occurred
327
         * within the 500ms. Cannot mouseup between touchstart and
328
         * touchend, nor in the 500ms after touchend. */
329
        touches: 0,
330
331
        allowEvent: function(e) {
332
333
            var allow = true;
334
335
            if (/^(mousedown|mousemove)$/.test(e.type) && TouchHandler.touches) {
336
                allow = false;
337
            }
338
339
            return allow;
340
        },
341
        registerEvent: function(e) {
342
            var eType = e.type;
343
344
            if (eType === 'touchstart') {
345
346
                TouchHandler.touches += 1; // push
347
348
            } else if (/^(touchend|touchcancel)$/.test(eType)) {
349
350
                setTimeout(function() {
351
                    if (TouchHandler.touches) {
352
                        TouchHandler.touches -= 1; // pop after 500ms
353
                    }
354
                }, 500);
355
356
            }
357
        }
358
    };
359
360
361
    /**
362
     * Delegated click handler for .waves-effect element.
363
     * returns null when .waves-effect element not in "click tree"
364
     */
365
    function getWavesEffectElement(e) {
366
367
        if (TouchHandler.allowEvent(e) === false) {
368
            return null;
369
        }
370
371
        var element = null;
372
        var target = e.target || e.srcElement;
373
374
        while (target.parentElement) {
375
            if ( (!(target instanceof SVGElement)) && target.classList.contains('waves-effect')) {
1 ignored issue
show
Bug introduced by
The variable SVGElement seems to be never declared. If this is a global, consider adding a /** global: SVGElement */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
376
                element = target;
377
                break;
378
            }
379
            target = target.parentElement;
380
        }
381
382
        return element;
383
    }
384
385
    /**
386
     * Bubble the click and show effect if .waves-effect elem was found
387
     */
388
    function showEffect(e) {
389
390
        // Disable effect if element has "disabled" property on it
391
        // In some cases, the event is not triggered by the current element
392
        // if (e.target.getAttribute('disabled') !== null) {
393
        //     return;
394
        // }
395
396
        var element = getWavesEffectElement(e);
397
398
        if (element !== null) {
399
400
            // Make it sure the element has either disabled property, disabled attribute or 'disabled' class
401
            if (element.disabled || element.getAttribute('disabled') || element.classList.contains('disabled')) {
402
                return;
403
            }
404
405
            TouchHandler.registerEvent(e);
406
407
            if (e.type === 'touchstart' && Effect.delay) {
408
409
                var hidden = false;
410
411
                var timer = setTimeout(function () {
412
                    timer = null;
413
                    Effect.show(e, element);
414
                }, Effect.delay);
415
416
                var hideEffect = function(hideEvent) {
417
418
                    // if touch hasn't moved, and effect not yet started: start effect now
419
                    if (timer) {
420
                        clearTimeout(timer);
421
                        timer = null;
422
                        Effect.show(e, element);
423
                    }
424
                    if (!hidden) {
425
                        hidden = true;
426
                        Effect.hide(hideEvent, element);
427
                    }
428
429
                    removeListeners();
430
                };
431
432
                var touchMove = function(moveEvent) {
433
                    if (timer) {
434
                        clearTimeout(timer);
435
                        timer = null;
436
                    }
437
                    hideEffect(moveEvent);
438
439
                    removeListeners();
440
                };
441
442
                element.addEventListener('touchmove', touchMove, false);
443
                element.addEventListener('touchend', hideEffect, false);
444
                element.addEventListener('touchcancel', hideEffect, false);
445
446
                var removeListeners = function() {
447
                    element.removeEventListener('touchmove', touchMove);
448
                    element.removeEventListener('touchend', hideEffect);
449
                    element.removeEventListener('touchcancel', hideEffect);
450
                };
451
            } else {
452
453
                Effect.show(e, element);
454
455
                if (isTouchAvailable) {
456
                    element.addEventListener('touchend', Effect.hide, false);
457
                    element.addEventListener('touchcancel', Effect.hide, false);
458
                }
459
460
                element.addEventListener('mouseup', Effect.hide, false);
461
                element.addEventListener('mouseleave', Effect.hide, false);
462
            }
463
        }
464
    }
465
466
    Waves.init = function(options) {
467
        var body = document.body;
468
469
        options = options || {};
470
471
        if ('duration' in options) {
472
            Effect.duration = options.duration;
473
        }
474
475
        if ('delay' in options) {
476
            Effect.delay = options.delay;
477
        }
478
479
        if (isTouchAvailable) {
480
            body.addEventListener('touchstart', showEffect, false);
481
            body.addEventListener('touchcancel', TouchHandler.registerEvent, false);
482
            body.addEventListener('touchend', TouchHandler.registerEvent, false);
483
        }
484
485
        body.addEventListener('mousedown', showEffect, false);
486
    };
487
488
489
    /**
490
     * Attach Waves to dynamically loaded inputs, or add .waves-effect and other
491
     * waves classes to a set of elements. Set drag to true if the ripple mouseover
492
     * or skimming effect should be applied to the elements.
493
     */
494
    Waves.attach = function(elements, classes) {
495
496
        elements = getWavesElements(elements);
497
498
        if (toString.call(classes) === '[object Array]') {
499
            classes = classes.join(' ');
500
        }
501
502
        classes = classes ? ' ' + classes : '';
503
504
        var element, tagName;
505
506
        for (var i = 0, len = elements.length; i < len; i++) {
507
508
            element = elements[i];
509
            tagName = element.tagName.toLowerCase();
510
511
            if (['input', 'img'].indexOf(tagName) !== -1) {
512
                TagWrapper[tagName](element);
513
                element = element.parentElement;
514
            }
515
516
            if (element.className.indexOf('waves-effect') === -1) {
517
                element.className += ' waves-effect' + classes;
518
            }
519
        }
520
    };
521
522
523
    /**
524
     * Cause a ripple to appear in an element via code.
525
     */
526
    Waves.ripple = function(elements, options) {
527
        elements = getWavesElements(elements);
528
        var elementsLen = elements.length;
529
530
        options          = options || {};
531
        options.wait     = options.wait || 0;
532
        options.position = options.position || null; // default = centre of element
533
534
535
        if (elementsLen) {
536
            var element, pos, off, centre = {}, i = 0;
537
            var mousedown = {
538
                type: 'mousedown',
539
                button: 1
540
            };
541
            var hideRipple = function(mouseup, element) {
542
                return function() {
543
                    Effect.hide(mouseup, element);
544
                };
545
            };
546
547
            for (; i < elementsLen; i++) {
548
                element = elements[i];
549
                pos = options.position || {
550
                    x: element.clientWidth / 2,
551
                    y: element.clientHeight / 2
552
                };
553
554
                off      = offset(element);
555
                centre.x = off.left + pos.x;
556
                centre.y = off.top + pos.y;
557
558
                mousedown.pageX = centre.x;
559
                mousedown.pageY = centre.y;
560
561
                Effect.show(mousedown, element);
562
563
                if (options.wait >= 0 && options.wait !== null) {
564
                    var mouseup = {
565
                        type: 'mouseup',
566
                        button: 1
567
                    };
568
569
                    setTimeout(hideRipple(mouseup, element), options.wait);
570
                }
571
            }
572
        }
573
    };
574
575
    /**
576
     * Remove all ripples from an element.
577
     */
578
    Waves.calm = function(elements) {
579
        elements = getWavesElements(elements);
580
        var mouseup = {
581
            type: 'mouseup',
582
            button: 1
583
        };
584
585
        for (var i = 0, len = elements.length; i < len; i++) {
586
            Effect.hide(mouseup, elements[i]);
587
        }
588
    };
589
590
    /**
591
     * Deprecated API fallback
592
     */
593
    Waves.displayEffect = function(options) {
594
        console.error('Waves.displayEffect() has been deprecated and will be removed in future version. Please use Waves.init() to initialize Waves effect');
595
        Waves.init(options);
596
    };
597
598
    return Waves;
599
});
600